home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 42 / Amiga Format AFCD42 (Issue 126, Aug 1999).iso / -serious- / programming / basic / udpchat / old_stuff / udp_chatv1.6.asc < prev    next >
Encoding:
Text File  |  1999-05-14  |  14.4 KB  |  492 lines

  1. ;
  2. ;                      UDP Chat code V1.6   25/12/97
  3. ;
  4. ;    This code sends and receives UDP data packets like a
  5. ;  a simple IRC client, and  checks wether they have arrived
  6. ;  at their destination.
  7. ;
  8. ;  Written by Anton Reinauer <anton@ww.co.nz>.
  9. ;  GUI: Alvaro Thompson <alvaro@enterprise.net> - And awful hacks
  10. ;  by me to his nice font sensitive code :-)
  11. ;        - typical bloody games programmer ;-)
  12. ;
  13. ;  Thanks to Paul Burkey for TCP_Funcs and to Dr. Ercole Spiteri
  14. ;  for TCP-to-Blitz.
  15. ;
  16. ;  Turn overflow errors off in Debugger options
  17.  
  18.  
  19. WBStartup
  20. NoCli
  21. Hostname.s="localhost"  ; default destination host address
  22. #PORT=3001              ; default destination port to send data to
  23. #LOCALPORT=3001
  24. #NO_CONNECTION=1        ; are we connected, or have we been connected to the Internet?
  25.                         ; 1 for not been connected, 0 for have/are connected.
  26.  
  27. INCLUDE "TCPFuncs.bb"
  28. INCLUDE "Net_Protocol_Header.bb2"  ; Net protocol constants
  29. DEFTYPE .w
  30.  
  31. packet_number.l=0
  32.  
  33. ;********************************************************
  34.  
  35. NEWTYPE .message_status
  36.   number.l   ; packet number
  37.   ack.w        ; has it been received yet?
  38.   timestamp.l  ; when was it sent?
  39.   message.s
  40.   resends.w
  41. End NEWTYPE
  42.  
  43. ;*******************************************************
  44. .Dims
  45.  
  46. Dim messages.message_status(100)
  47.  
  48. ;*******************************************************
  49.  
  50. .Print_String
  51. Statement Print_String{text$}
  52.   SHARED ypos
  53.   If ypos<200
  54.     ypos+10
  55.   Else
  56.     WScroll 10,60,590,220,0,10
  57.   EndIf
  58.  
  59.   WLocate 10,ypos
  60.   Print text$               ; print string received
  61. End Function
  62.  
  63. .WriteUDP
  64. Statement WriteUDP{ad.l,size.w}
  65.   SHARED sock.l,host.sockaddrin,hostlen.l
  66.  
  67.   ; This routine writes data via UDP
  68.  
  69.   sockwrite.l=0                         ;Clear Writemask
  70.   sockwrite.l BitSet sock.l             ;set Writemask on our socket
  71.   g=WaitSelect_(2,0,&sockwrite.l,0,0,0) ;Wait until server is ready to read our data
  72.   c=sendto_(sock.l,ad,size,0,host,hostlen)             ;Send data to server
  73.  
  74. End Statement
  75.  
  76. .Send_String_Message
  77. Statement Send_String_Message{send_string.s}
  78.  SHARED packet_number,messages(),free_message,last_message_number
  79.  
  80.   send$=Str$(#REL_STRING_END) + Mkl$(packet_number) + send_string + Chr$(0)
  81.   messages(free_message)\number=packet_number,False,Ticks,send$
  82.   If messages(last_message_number)\ack=True
  83.     last_message_number=free_message
  84.   EndIf
  85.   WriteUDP{&send$,Len(send$)}
  86.   packet_number+1
  87.   free_message+1
  88.   If free_message=101 Then free_message=0
  89.  
  90. End Statement
  91.  
  92. .ReadUDP
  93. Function .s ReadUDP{}
  94.   SHARED sock.l,TCPmem.l
  95.  
  96.   ; This Function reads data from the socket it is bound to.
  97.   ; If there is no messages then it will return an empty string =""
  98.  
  99.   sockread.l=0                              ;Clear Readmask
  100.   sockread.l BitSet sock.l                  ;Set Readmask on our socket
  101.   e=IoctlSocket_(sock.l,#FIONREAD,TCPmem.l) ;How much data is there?
  102.   f.l=Peek.l(TCPmem.l)                      ;Place value in f
  103.  
  104.   If f>0                                    ; any data waiting
  105.  
  106.     c=recvfrom_(sock.l,TCPmem.l,f,0,0,messagelen)  ;Read all data waiting on socket
  107.  
  108.     If c>0    ; if there any data waiting
  109.         a=0 : c$=""
  110.         Repeat
  111.             c$=c$+Chr$(Peek.b(TCPmem+a)) : a+1    ; build string
  112.         Until a=c
  113.     EndIf
  114.  
  115.   EndIf
  116.  
  117.   Function Return c$
  118. End Function
  119.  
  120. .Get_Host
  121. Statement Get_Host{host$,port.w}
  122.   SHARED host.sockaddrin,hostlen.l,hostexists
  123.  
  124.   *a.hostent=gethostbyname_(host$)     ; set up destination address to send packets to
  125.   If *a.hostent=0
  126.     hostexists=False
  127.     WLocate 10,18
  128.     Print "    No connection to host           "
  129.     Statement Return
  130.   Else
  131.     hostexists=True
  132.     WLocate 10,18
  133.     Print "Type in data, and hit return to send"
  134.   EndIf
  135.  
  136.   ;Copy Details to our Sockaddrin structure
  137.  
  138.   bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host.sockaddrin\sin_addr,*a.hostent\h_length)
  139.  
  140.   host.sockaddrin\sin_port=port       ;Set port number
  141.   host.sockaddrin\sin_family=2        ;Set type to AT_INET
  142.   hostlen.l=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  143. End Function
  144.  
  145. .Localhost_Name
  146. Function.s Localhost_Name{}
  147.     If OpenFile (0,"ENV:HOSTNAME")
  148.        FileInput 0
  149.        While NOT Eof(0)
  150.          a$=a$+Inkey$
  151.        Wend
  152.        CloseFile 0
  153.        WindowInput 0
  154.        Function Return a$
  155.     EndIf
  156.     Function Return "localhost"
  157. End Function
  158.  
  159. .ConnectUDP
  160. Function ConnectUDP{host$,port.w}
  161.   SHARED sock.l,host.sockaddrin,hostlen.l,port_used,bsd_library_address.l
  162.   ;
  163.   ; Connect to host at specified port
  164.   ; Return true or False if Connection is made
  165.   ;
  166.  
  167.   bsd_library_address.l=OpenLibrary_("bsdsocket.library",0)
  168.   If bsd_library_address=0
  169.     Function Return False
  170.   Else
  171.       sock.l=socket_(2,2,0)        ; open UDP socket
  172.       If sock=-1
  173.         Function Return False
  174.       Else
  175.           *a.hostent=gethostbyname_(host$)     ; set up destination address to send packets to
  176.           If *a.hostent=0
  177.              Function Return False
  178.           EndIf
  179.                                                                                                                       inY=WBHeight
  180.           ;Copy Details to our Sockaddrin structure
  181.  
  182.           bb=CopyMem_(*a.hostent\h_addr_list\ItemA,&host.sockaddrin\sin_addr,*a.hostent\h_length)
  183.  
  184.           host.sockaddrin\sin_port=port       ;Set port number
  185.           host.sockaddrin\sin_family=2        ;Set type to AT_INET
  186.           hostlen.l=SizeOf.sockaddrin        ;Get length of structure sockaddrin
  187.  
  188.           temp=0
  189.           port_used=port
  190.           Repeat
  191.             host.sockaddrin\sin_port=port_used  ; for testing on localhost- keep trying one port at a time after #LOCALPORT for
  192.             If bind_(sock,host,hostlen)=0    ; bind socket to port so we can                                     free port
  193.               Function Return True           ; receive data on port
  194.             Else
  195.               port_used+1
  196.               temp+1
  197.             EndIf
  198.           Until temp=10
  199.       EndIf
  200.   EndIf
  201. End Function
  202.  
  203. .Resend_Message
  204. Function Resend_Message{message_number}
  205.   SHARED messages()
  206.   messages(message_number)\timestamp=Ticks
  207.   messages(message_number)\resends+1
  208.   If messages(message_number)\resends>5  ; is host not responding?
  209.      Print_String{"Host not responding to Packet no: " + Str$(message_number)}
  210.      Function Return False
  211.   Else
  212.      WriteUDP{&messages(message_number)\message,Len(messages(message_number)\message)}
  213.      Print_String{"Resent Packet no:" + Str$(message_number)}
  214.      Function Return True
  215.   EndIf
  216. End Statement
  217.  
  218. .Find_Next_Message
  219. Statement Find_Next_Message{}
  220. SHARED last_message_number,messages(),free_message
  221.  
  222.   exit=0
  223.   Repeat              ; find next unacknowledged message in array
  224.  
  225.     last_message_number+1
  226.     If last_message_number=101 Then last_message_number=0
  227.  
  228.     If last_message_number=free_message  ; if have got to free_message then there are no messages waiting to be acknowledged
  229.       If last_message_number=0
  230.         last_message_number=100
  231.         exit=1
  232.       Else
  233.         last_message_number-1
  234.         exit=1
  235.       EndIf
  236.     Else
  237.       If messages(last_message_number)\ack=False   ; we've found the next unacknowledged message in the array
  238.         exit=1
  239.       EndIf
  240.     EndIf
  241.  
  242.   Until exit=1
  243. End Statement
  244.  
  245. .Acknowledge_Packet
  246. Statement Acknowledge_Packet{ack_packet_number}
  247.   SHARED last_message_number,free_message,messages()
  248.  
  249.   a=last_message_number
  250.   exit=0
  251.   Repeat
  252.     If messages(a)\number=ack_packet_number   ; find message in sent messages array
  253.       messages(a)\ack=True         ; note it as being received
  254.       Format "0000"
  255.       current_time.l=Ticks
  256.       current_lag=current_time-messages(a)\timestamp ; calculate lag
  257.       b$=Str$(current_lag)
  258.       WLocate 142,32
  259.       Print b$         ; print lag onscreen
  260.       Format""
  261.       exit=1
  262.     EndIf
  263.     a+1
  264.     If a=101 Then a=0    ; if at end of array, jump to beginning
  265.   Until exit=1
  266.  
  267.   If a=last_message_number+1 OR (a=0 AND last_message_number=100)   ; if last message unacknowledged is acknowledged
  268.     Find_Next_Message{}                                             ; then find next unacknowledged message
  269.   Else                ; we have lost a packet- resend last packet number!
  270.     If messages(last_message_number)\timestamp-current_time > current_lag+5    ; if its behind the current lag resend it
  271.       Print_String{"Received packet ACK out of order, resending last unacknowledged packet."}
  272.       If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
  273.         messages(last_message_number)\timestamp=current_time+500  ; stop it resending the packet infinitely
  274.         Acknowledge_Packet{last_message_number}       ; wipe message off message acknowledge array
  275.       EndIf
  276.     EndIf
  277.   EndIf
  278. End Statement
  279.  
  280. .Decode_Packet
  281. Statement Decode_Packet{incoming_string.s}
  282.   packet_type=Val(Left$(incoming_string,1))
  283.   Select packet_type
  284.  
  285.     Case #REL_STRING_END
  286.       packet_number.l=Cvl(Mid$(incoming_string,2,4))
  287.       num$=Str$(packet_number)
  288.       a$=num$ + ": " + Mid$(incoming_string,6,Len(incoming_string)-6)
  289.       send$=Str$(#REL_PACKET_ACK) + Mkl$(packet_number)
  290.       WriteUDP{&send$,Len(send$)}
  291.  
  292.     Case #REL_PACKET_ACK
  293.          packet_number.l=Cvl(Mid$(incoming_string,2,4))
  294.          a$=": Packet no. " + Str$(packet_number) + " arrived at destination."
  295.          Acknowledge_Packet{packet_number}
  296.  
  297.   End Select
  298.  
  299.   a$="R "+a$
  300.   Print_String{a$}
  301. End Statement
  302.  
  303. ;****************************************
  304.  
  305.  
  306. Gosub Init_Gui
  307.  
  308. ypos=40
  309. xpos=10
  310. free_message=0
  311. messages(free_message)\ack=True
  312. last_message_ack=0
  313.  
  314. CNIF #NO_CONNECTION=1    ; if not connected to the `net
  315.  
  316.   If ConnectUDP {Hostname,#LOCALPORT}              ; bind socket to local port
  317.      Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
  318.      WLocate 510,4
  319.      Print "Bound to: ",port_used
  320.   Else
  321.      Print_String { "Error in setting up UDP at Port "+ Str$(port_used)}
  322.      Goto Exit
  323.   EndIf
  324.  
  325.   Get_Host {Hostname,#PORT}
  326.  
  327. CELSE
  328.   If ConnectUDP {Localhost_Name{},#LOCALPORT}              ; bind socket to local port
  329.      Print_String {"Bound to "+Hostname+" "+ Str$(port_used)}
  330.      WLocate 510,4
  331.      Print "Bound to: ",port_used
  332.   Else
  333.      Print_String { "Error in setting up UDP at Port "+ Str$(port_used)}
  334.      Goto Exit
  335.   EndIf
  336.  
  337.   Get_Host {Localhost_Name{},#PORT}
  338. CEND
  339.  
  340. ResetTimer
  341.  
  342. ;****************************************
  343.  
  344. .Main
  345.  
  346. Repeat
  347.  
  348.     VWait
  349.  
  350.     If ev.l=$40
  351.       Select GadgetHit
  352.         Case51
  353.           Get_Host{GTGetString(0,51), GTGetInteger(0,52)}
  354.         Case52
  355.           Get_Host{GTGetString(0,51), GTGetInteger(0,52)}
  356.         Case53           ; Delay 1s
  357.           VWait 50
  358.         Case54
  359.           a$=Localhost_Name {}   ;
  360.           WLocate 315+pingwidth+15,hostnamey+3
  361.           Print a$
  362.         Case63       ; Delay 5s
  363.           VWait 2000   ; delay 40s
  364.         Case64
  365.          If hostexists=True
  366.              send$=GTGetString(0,64)
  367.              Print_String{"S " +Str$(packet_number) +": "+ send$}
  368.              Send_String_Message{send$}          ; Send String
  369.          EndIf
  370.        End Select
  371.     EndIf
  372.  
  373.     a$=ReadUDP{}   ; get data from socket
  374.  
  375.     If a$<>""
  376.        Decode_Packet{a$}
  377.     EndIf
  378.  
  379.     If messages(last_message_number)\ack=False  ; only check if there's messages that haven't been acknowledged yet.
  380.       If Ticks>messages(last_message_number)\timestamp+250  ; has it been 5 secs since the packet was sent?
  381.         If Resend_Message{last_message_number}= False   ; if run out of resends- time out message
  382.           Acknowledge_Packet{message_number}            ; wipe message off message acknowledge array
  383.         EndIf
  384.       EndIf
  385.     EndIf
  386.  
  387.     ev=Event
  388.  
  389. Until ev=$200    ; shut down if close gadget hit
  390.  
  391. CloseTCP{}                          ; close connection
  392.  
  393. dummy.l=CloseLibrary_(bsd_library_address)  ; close bsdsocket.library
  394.                                        ; or else Miami can't exit
  395. Exit:
  396. WLocate 10,18
  397. Print "                                    "
  398. WLocate 10,18
  399. Print"Socket closed"
  400. Delay_(30)
  401.  
  402. FreeMem TCPmem,$2000
  403.  
  404. End
  405.  
  406. ;****************************************
  407.  
  408. .Init_Gui
  409.     WBenchToFront_
  410.     WbToScreen0
  411.     DefaultIDCMP $20|$40|$200
  412.     *scr.Screen=Peek.l(Addr Screen(0))
  413.     *myfont.TextAttr=*scr\Font
  414.     fontheight=*myfont\ta_YSize
  415.     ad1.l=*myfont\ta_Name
  416.     fontname$=Peek$(ad1)
  417.  
  418.     LoadFont 1,fontname$,fontheight,0
  419.  
  420.     portwidth=TextLength_(*scr\_RastPort,"Port:",5)+10
  421.     numberswidth=TextLength_(*scr\_RastPort,"99999",5)+14
  422.     serverwidth=TextLength_(*scr\_RastPort,"Send To:",8)+10
  423.     connectwidth=TextLength_(*scr\_RastPort,"Connect",7)+14
  424.     disconnectwidth=TextLength_(*scr\_RastPort,"Disconnect",10)+14
  425.     pingwidth=TextLength_(*scr\_RastPort,"Localhost:",11)
  426.     delaywidth=TextLength_(*scr\_RastPort,"Delay 1s",8)+10
  427.     inputwidth=TextLength_(*scr\_RastPort,"Input:",6)+10
  428.     firstperson=TextLength_(*scr\_RastPort,"1:",2)+10
  429.  
  430.     #SERVERBTN       =51
  431.     #PORTBTN         =52
  432.     #BUTTON          =53
  433.  
  434.     x=1
  435.     y=1
  436.  
  437.     x1=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+5
  438.  
  439.     If #NO_CONNECTION=0
  440.       GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,Localhost_Name {}
  441.     Else
  442.       GTString  0,#SERVERBTN,x+serverwidth,y,serverwidth*2,fontheight+4,"Send To:",1,256,"localhost"
  443.     EndIf
  444.     GTInteger 0,#PORTBTN,x+(serverwidth*3)+portwidth+3,y,numberswidth,fontheight+4,"Port:",1,#PORT
  445.     GTButton  0,#BUTTON,345,y,delaywidth,fontheight+4,"Delay 1s",$10
  446.     GTButton  0,63,340+delaywidth+10,y,delaywidth,fontheight+4,"Delay 5s",$10
  447.     y+fontheight+5
  448.  
  449.     hostnamey=y
  450.  
  451.     GTButton  0,54,315,y,pingwidth,fontheight+4,"Localhost:",$10
  452.  
  453.     WinWidth=x+(serverwidth*3)+portwidth+numberswidth+9+3+connectwidth+8+disconnectwidth+8+90
  454.  
  455.     y+fontheight+5
  456.  
  457.     GTButton  0,55,120,y,firstperson,fontheight+4,"1:",$10
  458.     GTButton  0,56,180,y,firstperson,fontheight+4,"2:",$10
  459.     GTButton  0,57,240,y,firstperson,fontheight+4,"3:",$10
  460.     GTButton  0,58,300,y,firstperson,fontheight+4,"4:",$10
  461.     GTButton  0,59,360,y,firstperson,fontheight+4,"5:",$10
  462.     GTButton  0,60,420,y,firstperson,fontheight+4,"6:",$10
  463.     GTButton  0,61,480,y,firstperson,fontheight+4,"7:",$10
  464.     GTButton  0,62,540,y,firstperson,fontheight+4,"8:",$10
  465.  
  466.     GTString  0,64,50,215,550,fontheight+4,"Send:",1,67,""
  467.  
  468.     WinHeight=y+fontheight+5
  469.  
  470.     ;WinX=WBWidth/2-(WinWidth/2)
  471.     ;WinY=WBHeight/2-(WinHeight/2)
  472.  
  473.     WinTitle$="UDP Send"
  474.     ;ScreenTitle$="UDP Send "+Chr$(169)+"1997."
  475.  
  476.     Window 0,0,10,WinWidth,245,$0002|$0004|$0008,WinTitle$,1,2
  477.     Use Window 0:Activate 0:AttachGTList 0,0:WTitle WinTitle$,ScreenTitle$:Menus Off
  478.  
  479.     WindowOutput0
  480.     WindowInput 0
  481.  
  482.     a$=Localhost_Name {}   ;
  483.     WLocate 315+pingwidth+15,hostnamey+3
  484.     Print a$
  485.  
  486.     WLocate 10,y+3
  487.     Print"Ping Time-VBL"
  488.  
  489. Return
  490.  
  491.  
  492.